package com.didi.gulimall.cart.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.didi.gulimall.api.CouponApiService;
import com.didi.gulimall.api.ProductApiService;
import com.didi.gulimall.api.WareApiService;
import com.didi.gulimall.cart.service.CartService;
import com.didi.gulimall.cart.vo.Cart;
import com.didi.gulimall.cart.vo.CartItem;
import com.didi.gulimall.cart.vo.UserInfoTo;
import com.didi.gulimall.common.constant.BizCodeEnume;
import com.didi.gulimall.common.constant.CartConstant;
import com.didi.gulimall.common.constant.SystemConstant;
import com.didi.gulimall.common.exception.DuplicateFieldsException;
import com.didi.gulimall.common.to.OrderBackVo;
import com.didi.gulimall.common.to.SkuInfoTO;
import com.didi.gulimall.common.utils.LogUtils;
import com.didi.gulimall.common.utils.R;
import com.didi.gulimall.common.vo.OrderItemVo;
import com.didi.gulimall.common.vo.OrderVo;
import com.didi.gulimall.interceptor.CartInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * @author 10626
 */
@Service
@Slf4j
public class CartServiceImpl implements CartService {
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private ThreadPoolExecutor executor;

    @Autowired
    private ProductApiService productApiService;

    @Autowired
    private WareApiService wareApiService;

    @Autowired
    private CouponApiService cuponApiService;

    @Value("${gulimall.item}")
    private String itemHtml;


    @Override
    public void addToCart(Long skuId, Integer num) throws ExecutionException, InterruptedException {
        BoundHashOperations<String, String, String> cartOps = getCartOps();
        CartItem cartItem = getCartItem(skuId);
        String jsonStr = "";
        if (cartItem != null) {
            cartItem.setCount(cartItem.getCount() + num);
            jsonStr = JSON.toJSONString(cartItem);
        } else {
            CartItem cart = new CartItem();
            cart.setCount(num);
            CompletableFuture<Void> skuInfoFuture = CompletableFuture.runAsync(() -> {
                SkuInfoTO skuInfoTO = getProductItem(skuId);
                BeanUtils.copyProperties(skuInfoTO, cart);
                cart.setTotalPrice(skuInfoTO.getPrice().multiply(new BigDecimal(num.toString())));
            }, executor);
            CompletableFuture<Void> valuesFuture = CompletableFuture.runAsync(() -> {
                R stringValue = productApiService.getStringValue(skuId);
                if (stringValue.getCode() != 0) {
                    throw new DuplicateFieldsException(stringValue.getData(SystemConstant.MSG, new TypeReference<String>() {
                    }));
                }
                List<String> values = stringValue.getData("values", new TypeReference<List<String>>() {
                });
                cart.setSkuAttr(values);
            }, executor);
            CompletableFuture.allOf(skuInfoFuture, valuesFuture).get();
            jsonStr = JSON.toJSONString(cart);
        }
        cartOps.put(skuId.toString(), jsonStr);
    }

    private BoundHashOperations<String, String, String> getCartOps() {
        UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
        String cartKey = "";
        if (userInfoTo.getUserId() != null) {
            cartKey = CartConstant.CART_PREFIX + userInfoTo.getUserId();
        } else {
            cartKey = CartConstant.CART_PREFIX + userInfoTo.getUserKey();
        }
        return redisTemplate.boundHashOps(cartKey);
    }

    @Override
    public CartItem getCartItem(Long skuId) {
        BoundHashOperations<String, String, String> cartOps = getCartOps();
        String json = (String) cartOps.get(skuId + "");
        if (StringUtils.isNotBlank(json)) {
            return JSON.parseObject(json, CartItem.class);
        } else {
            return null;
        }
    }

    @Override
    public String getCartList(Model model) throws ExecutionException, InterruptedException {
        ThreadLocal<UserInfoTo> threadLocal = CartInterceptor.threadLocal;
        UserInfoTo userInfoTo = threadLocal.get();
        Long userId = userInfoTo.getUserId();
        String temporary = CartConstant.CART_PREFIX + userInfoTo.getUserKey();
        List<CartItem> total = getCartItems(temporary);
        if (userId != null) {
            if (total.size() > 0) {
                for (CartItem cartItem : total) {
                    addToCart(cartItem.getSkuId(), cartItem.getCount());
                }
            }
            total = getCartItems(CartConstant.CART_PREFIX + userInfoTo.getUserId());
            redisTemplate.delete(temporary);
        }
        // 选中的优惠信息
        List<CartItem> collect = total.stream().filter(CartItem::getCheck).collect(Collectors.toList());
        Long levelId = userInfoTo.getLevelId() == null ? CartConstant.DEFAULT_LEVEL : userInfoTo.getLevelId();
        R r = cuponApiService.getPromotionAmount(levelId, collect);
        Cart cart = r.getData(new TypeReference<Cart>() {
        });
        cart.setItems(total);
        log.info("购物车数据:{}", JSON.toJSONString(cart));
        model.addAttribute("cartItems", cart);
        return "cartList";
    }

    @Override
    public void changeItemCount(Long skuId, Integer num) {
        BoundHashOperations<String, String, String> cartOps = getCartOps();
        String json = (String) cartOps.get(skuId.toString());
        CartItem cartItem = JSON.parseObject(json, CartItem.class);
        cartItem.setCount(num);
        String s = JSON.toJSONString(cartItem);
        cartOps.put(skuId.toString(), s);
    }

    @Override
    public void checkItem(Long skuId, Integer check) {
        BoundHashOperations<String, String, String> cartOps = getCartOps();
        String json = (String) cartOps.get(skuId.toString());
        CartItem cartItem = JSON.parseObject(json, CartItem.class);
        cartItem.setCheck(check == 1);
        String s = JSON.toJSONString(cartItem);
        cartOps.put(skuId.toString(), s);
    }

    @Override
    public void removeItem(Long skuId) {
        BoundHashOperations<String, String, String> cartOps = getCartOps();
        cartOps.delete(skuId.toString());
    }

    @Override
    public R memberCartItems() {
        ThreadLocal<UserInfoTo> threadLocal = CartInterceptor.threadLocal;
        UserInfoTo userInfoTo = threadLocal.get();
        String userKey = CartConstant.CART_PREFIX + userInfoTo.getUserId();
        List<CartItem> cartItems = getCartItems(userKey);
        try {
            cartItems = cartItems.stream().filter(CartItem::getCheck)
                    .peek(cartItem -> {
                        R info = productApiService.info(cartItem.getSkuId());
                        if (info.getCode() != 0) {
                            throw new DuplicateFieldsException(info.getData(SystemConstant.MSG, new TypeReference<String>() {
                            }));
                        }
                        SkuInfoTO skuInfo = info.getData("skuInfo", new TypeReference<SkuInfoTO>() {
                        });
                        BeanUtils.copyProperties(skuInfo, cartItem);
                    }).collect(Collectors.toList());
            return R.ok().put("cartItems", cartItems);
        } catch (Exception e) {
            log.error(LogUtils.getStackTraceInfo(e));
            return R.error(BizCodeEnume.RPC_FIND_PRODUCT.getMsg());
        }
    }

    @Override
    public R memberListNoUpdateProduct() {
        ThreadLocal<UserInfoTo> threadLocal = CartInterceptor.threadLocal;
        UserInfoTo userInfoTo = threadLocal.get();
        String userKey = CartConstant.CART_PREFIX + userInfoTo.getUserId();
        List<CartItem> cartItems = getCartItems(userKey);
        R hasstock = wareApiService.getSkusHasStockbByCartItem(cartItems);
        if (hasstock.getCode() != 0) {
            throw new DuplicateFieldsException(hasstock.getData(SystemConstant.MSG, new TypeReference<String>() {
            }));
        }
        Map<Long, Boolean> map = hasstock.getData("hasstock", new TypeReference<Map<Long, Boolean>>() {
        });
        cartItems = cartItems.stream().filter(CartItem::getCheck).filter(cartItem -> map.get(cartItem.getSkuId())).collect(Collectors.toList());
        return R.ok().put("cartItems", cartItems);
    }

    @Override
    public void orderBack(OrderBackVo orderBackVo) {
        String cartKey = CartConstant.CART_PREFIX + orderBackVo.getMemberId();
        log.info("绑定的key:{}", cartKey);
        BoundHashOperations<String, Object, Object> boundHashOps = redisTemplate.boundHashOps(cartKey);
        List<OrderItemVo> orderItemVos = orderBackVo.getOrderItemVos();
        for (OrderItemVo orderItemVo : orderItemVos) {
            boundHashOps.putIfAbsent(orderItemVo.getSkuId().toString(), JSON.toJSONString(orderItemVo));
        }
    }

    @Override
    public R orderRemoveItem(List<CartItem> items) {
        log.info("要删除的商品信息{}", JSON.toJSONString(items));
        BoundHashOperations<String, String, String> cartOps = getCartOps();
        items.forEach(cartItem -> {
            String json = cartOps.get(cartItem.getSkuId().toString());
            CartItem item = JSON.parseObject(json, CartItem.class);
            if (item.getCount() > cartItem.getCount()) {
                item.setCount(item.getCount() - cartItem.getCount());
                cartOps.put(cartItem.getSkuId().toString(), JSON.toJSONString(item));
            } else {
//                cartOps.delete(item.getSkuId().toString());
            }
        });
        return R.ok();
    }

    @Override
    public void addCartCache(Long skuId, Integer num) throws ExecutionException, InterruptedException {
        BoundHashOperations<String, String, String> cartOps = getCartOps();
        CartItem cartItem = getCartItem(skuId);
        String jsonStr = "";
        if (cartItem != null) {
            cartItem.setCount(cartItem.getCount() + num);
            jsonStr = JSON.toJSONString(cartItem);
        } else {
            CartItem cart = new CartItem();
            cart.setCount(num);
            CompletableFuture<Void> skuInfoFuture = CompletableFuture.runAsync(() -> {
                SkuInfoTO skuInfoTO = getProductItem(skuId);
                BeanUtils.copyProperties(skuInfoTO, cart);
                cart.setTotalPrice(skuInfoTO.getPrice().multiply(new BigDecimal(num.toString())));
            }, executor);
            CompletableFuture<Void> valuesFuture = CompletableFuture.runAsync(() -> {
                R stringValue = productApiService.getStringValue(skuId);
                if (stringValue.getCode() != 0) {
                    throw new DuplicateFieldsException(stringValue.getData(SystemConstant.MSG, new TypeReference<String>() {
                    }));
                }
                List<String> values = stringValue.getData("values", new TypeReference<List<String>>() {
                });
                cart.setSkuAttr(values);
            }, executor);
            CompletableFuture.allOf(skuInfoFuture, valuesFuture).get();
            UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
            Long levelId = CartConstant.DEFAULT_LEVEL;
            if (userInfoTo.getLevelId() != null) {
                levelId = userInfoTo.getLevelId();
            }
            List<CartItem> cartItems = new ArrayList<>();
            cartItems.add(cart);
            R promotionAmount = cuponApiService.getPromotionAmount(levelId, cartItems);
            OrderVo data = promotionAmount.getData(new TypeReference<OrderVo>() {
            });
            OrderItemVo orderItemVo = data.getItems().get(0);
            jsonStr = JSON.toJSONString(orderItemVo);
        }
        cartOps.put(skuId.toString(), jsonStr);
    }

    private List<CartItem> getCartItems(String userKey) {
        List<CartItem> cartItems = new ArrayList<>();
        BoundHashOperations<String, Object, Object> boundHashOps = redisTemplate.boundHashOps(userKey);
        List<Object> values = boundHashOps.values();
        if (values != null) {
            for (Object value : values) {
                CartItem cartItem = JSON.parseObject((String) value, CartItem.class);
                cartItems.add(cartItem);
            }
        }
        return cartItems;
    }

    private SkuInfoTO getProductItem(Long skuId) {
        R info = productApiService.info(skuId);
        if (info.getCode() != 0) {
            throw new DuplicateFieldsException(info.getData(SystemConstant.MSG, new TypeReference<String>() {
            }));
        }
        SkuInfoTO skuInfo = info.getData("skuInfo", new TypeReference<SkuInfoTO>() {
        });
        if (skuInfo == null) {
            throw new DuplicateFieldsException(BizCodeEnume.NOT_FIND_PRODUCT.getMsg());
        }
        return skuInfo;

    }
}
